﻿using System.ComponentModel;
using Castle.DynamicProxy;

namespace Framework.Core.Notification
{
    public interface IAutoNotifyPropertyChanged : INotifyPropertyChanged
    {
        void OnPropertyChanged(string propertyName);
    }

    public class AutoNotifyPropertyChanged : IInterceptor
    {
        private const string SetterPrefixe = "set_";

        public void Intercept(IInvocation invocation)
        {
            var methodName = invocation.Method.Name;

            if (!IsSetter(methodName))
            {
                invocation.Proceed();
                return;
            }

            var oldValue = GetValue(invocation);

            invocation.Proceed();

            var newValue = GetValue(invocation);

            if (!Equals(oldValue, newValue))
            {
                GetNotifyPropertyChanged(invocation).OnPropertyChanged(GetPropertyName(methodName));
            }
        }

        private static IAutoNotifyPropertyChanged GetNotifyPropertyChanged(IInvocation invocation)
        {
            var target = invocation.Proxy as IAutoNotifyPropertyChanged;
            if (target == null)
            {
                throw new NotificationException(string.Format("Type {0} must implement {1}",
                                                              invocation.InvocationTarget.GetType(),
                                                              typeof (IAutoNotifyPropertyChanged)));
            }
            return target;
        }

        private static bool IsSetter(string methodName)
        {
            return methodName.StartsWith(SetterPrefixe);
        }

        private static object GetValue(IInvocation invocation)
        {
            var methodName = invocation.Method.Name;
            var propertyName = GetPropertyName(methodName);

            var target = invocation.Proxy;
            var property = target.GetType().GetProperty(propertyName);

            return property.GetValue(target, null);
        }

        private static string GetPropertyName(string methodName)
        {
            return methodName.Replace(SetterPrefixe, string.Empty);
        }
    }
}